//
//  CKImage.m
//  ConnectionKit
//
//  Created by Matthias Plappert on 13.06.09.
//  Copyright 2009 phapswebsolutions. All rights reserved.
//


#import "CKImage.h"
#import "CKCache.h"

#import "NSString+CKString.h"

@implementation CKImage

static NSArray *_supportedMIMETypes;

@synthesize size, URL, image, errorImage, cache, status;
@synthesize delegate;

+ (void)initialize
{
	if (self == [CKImage class]) {
		// These formats are supported by UIImage and therefor are supported by this class
		_supportedMIMETypes = [[NSArray alloc] initWithObjects:@"image/tiff", @"image/x-tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/bmp", @"image/x-windows-bmp", @"image/x-icon", @"image/x-xbitmap", @"image/x-xbm", @"image/xbm", nil];
	}
}

- (id)init
{
	if (self = [super init]) {
		_data = [[NSMutableData alloc] init];
		cache = YES;
		
		// Register for memory warning
		[[NSNotificationCenter defaultCenter] addObserver:self
												 selector:@selector(didReceiveMemoryWarning)
													 name:UIApplicationDidReceiveMemoryWarningNotification
												   object:nil];
	}
	return self;
}

- (void)dealloc
{
	[_connection cancel];
	[_connection release];
	delegate = nil;
	
	// Unregister for memory warnings
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	
	[URL release];
	[image release];
	[errorImage release];
	
	[_data release];
	
	[super dealloc];
}

- (void)didReceiveMemoryWarning
{	
	// Release the image to free memory
	self.image = nil;
}

- (void)cancel
{
	[_connection cancel];
}

#pragma mark -
#pragma mark Connection handling

- (void)load:(BOOL)synchronous
{
	if (self.status == CKImageStatusLoading) {
		#ifdef DEBUG
			NSLog(@"Image for %@ is already downloading...", self.URL);
		#endif
	} else {
		if (self.image == nil) {
			NSData *cachedData;
			if (self.cache == YES) {
				cachedData = [[CKCache sharedCache] dataForIdentifier:[self.URL md5Hash]];
			} else {
				cachedData = nil;
			}
			
			if (cachedData != nil) {
				#ifdef DEBUG
					NSLog(@"Image for %@ loaded from cache", self.URL);
				#endif
				
				UIImage *newImage = [[UIImage alloc] initWithData:cachedData];
				self.image = newImage;
				[newImage release];
			} else {
				#ifdef DEBUG
					NSLog(@"%@ not found in cache or cache is disabled. Now downloading...", self.URL);
				#endif
				
				status = CKImageStatusLoading;
				
				if (synchronous == YES) {
					// Download image from server
					NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.URL]
															 cachePolicy:NSURLRequestReturnCacheDataElseLoad
														 timeoutInterval:60.0];
					NSURLResponse *response; NSError *error;
					NSData *imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
					
					if (error != nil) {
						#ifdef DEBUG
							NSLog(@"Connection error while downloading %@: %@", self.URL, [error localizedDescription]);
						#endif
						
						status = CKImageStatusNotFound;
						self.image = self.errorImage;
						
						// Check for connection errors
						if ([self.delegate respondsToSelector:@selector(image:didFailWithError:)]) {
							[self.delegate image:self didFailWithError:error];
						}
					} else if ([self checkResponse:response] == YES) {
						// Save image in cache
						if (self.cache == YES) {
							[[CKCache sharedCache] setData:imageData forIdentifier:[self.URL md5Hash]];
						}
						
						// Create image
						UIImage *newImage = [[UIImage alloc] initWithData:imageData];
						self.image = newImage;
						[newImage release];
						
						status = CKImageStatusDidLoad;
						
						// Inform delegate
						if ([self.delegate respondsToSelector:@selector(imageDidReceiveData:)]) {
							[self.delegate imageDidReceiveData:self];
						}
					}
				} else {
					// Cancel and release old connection
					[_connection cancel];
					[_connection release];
					[_data setData:[NSData data]];
					
					// Start connection
					NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:self.URL]
															 cachePolicy:NSURLRequestReturnCacheDataElseLoad
														 timeoutInterval:60.0];
					_connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
					
					// Call delegate method
					if ([self.delegate respondsToSelector:@selector(imageWillReceiveData:)]) {
						[self.delegate imageWillReceiveData:self];
					}
				}
			}
		}
	}
}

- (BOOL)checkResponse:(NSURLResponse *)response
{
	NSString *MIMEType = [response MIMEType];
	if (![_supportedMIMETypes containsObject:MIMEType]) {
		#ifdef DEBUG
			NSLog(@"Response error while downloading %@: MIMEType is not %@ but %@", self.URL, _supportedMIMETypes, MIMEType);
		#endif
		
		status = CKImageStatusNotFound;
		self.image = self.errorImage;
		
		NSDictionary *userInfo = [NSDictionary dictionaryWithObject:response forKey:@"response"];
		NSError *error = [NSError errorWithDomain:@"de.phapswebsolutions.connectionkit" code:kCKImageUnsupportedMIMEType userInfo:userInfo];
		
		if ([self.delegate respondsToSelector:@selector(image:didFailWithError:)]) {
			[self.delegate image:self didFailWithError:error];
		}
		return NO;
	} else {
		return YES;
	}
}

#pragma mark -
#pragma mark Asynchronous connection handling

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
	if ([self checkResponse:response] == NO) {
		[connection cancel];
	}
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
	[_data appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
	// Cache image
	if (self.cache == YES) {
		[[CKCache sharedCache] setData:_data forIdentifier:[self.URL md5Hash]];
	}
	
	// Create image
	UIImage *newImage = [[UIImage alloc] initWithData:_data];
	self.image = newImage;
	[newImage release];
	
	// Release connection and data
	[_connection release];
	_connection = nil;
	[_data setData:[NSData data]];
	
	status = CKImageStatusDidLoad;
	
	// Inform delegate
	if ([self.delegate respondsToSelector:@selector(imageDidReceiveData:)]) {
		[self.delegate imageDidReceiveData:self];
	}
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
	#ifdef DEBUG
		NSLog(@"Connection error while downloading %@: %@", self.URL, [error localizedDescription]);
	#endif
	
	// Release connection and data
	[_connection release];
	_connection = nil;
	[_data setData:[NSData data]];
	
	status = CKImageStatusNotFound;
	self.image = self.errorImage;
	
	if ([self.delegate respondsToSelector:@selector(image:didFailWithError:)]) {
		[self.delegate image:self didFailWithError:error];
	}
}

@end
